package com.ailanyin.common.security.config;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.ailanyin.common.redis.service.RedisService;
import com.ailanyin.common.security.constants.SecurityConstant;
import com.ailanyin.common.security.filter.JwtAuthenticationTokenFilter;
import com.ailanyin.common.security.handle.LogoutHandler;
import com.ailanyin.common.security.handle.NoTokenResult;
import com.ailanyin.common.security.utils.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.PostConstruct;

/**
 * @author ailanyin
 * @version 1.0
 * @since 2022/11/25 0025 下午 16:37
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private RedisService redisService;
    @Autowired
    private NoTokenResult noTokenResult;
    @Autowired
    private LogoutHandler logoutHandler;

    @PostConstruct
    public void initSecret() {
        if (redisService.hasKey(SecurityConstant.REDIS_SECRET_KEY)) {
            TokenUtil.secret = SecureUtil.md5(SecureUtil.md5(redisService.get(SecurityConstant.REDIS_SECRET_KEY).toString()));
        } else {
            String random = RandomUtil.randomString(8);
            TokenUtil.secret = SecureUtil.md5(SecureUtil.md5(random));
            redisService.set(SecurityConstant.REDIS_SECRET_KEY, random);
        }
    }

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        //白名单
        for (String url : SecurityConstant.SECURITY_WHITELIST) {
            registry.antMatchers(url).permitAll();
        }

        // 由于使用的是JWT，我们这里不需要csrf
        httpSecurity.csrf()
                .disable()
                //添加自定义未授权和未登录结果返回
                .exceptionHandling()
                .authenticationEntryPoint(noTokenResult)
                .and()
                // 基于token，所以不需要session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //跨域请求会先进行一次options请求
                .antMatchers(HttpMethod.OPTIONS)
                .permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest()
                .authenticated();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 退出处理
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }

}
